/////////////////////////////////////////////
// ObjectARX defined commands

#include "StdAfx.h"
#include "StdArx.h"


// This is command 'CHKINTS'
void chkints()
{
	ads_point pickPt;
	ads_name ent;

	AcDbObjectId entId1, entId2;
	AcDbEntity *pEnt1, *pEnt2;
	AcGePoint3dArray intPoints;	// This array holds the intersecting points
	AcGePoint3d intPt;
	AcGePoint3d r1, r2, r3, r4;
	AcGeVector3d transVec(-0.1, 0.1, 0.0);	// Point translation vector
	AcGeMatrix3d transMat;					// Translation matrix
	char kw[10];
	int extVal = 0; // Extend value, 0 = no, 1 = fist entitity,
					// 2 = second entity, 3 = both entities
					// see intersectWith() function below

	int rc;

	rc = acedEntSel("\nSelect first entity to test for intersection: ",
					ent, pickPt);
	switch(rc)
	{
		case RTCAN:
			acutPrintf("\nUser canceled.");
			return;
		break;

		case RTERROR:
			acutPrintf("\nNothing selected.");
			return;
		break;
	}// switch

	acdbGetObjectId(entId1, ent);

	// Is the entity a line, arc or circle?
	// Here we limit ourselves to lines, arcs
	// and circles, because that is what we created
	// in the last application.
	acdbOpenAcDbEntity(pEnt1, entId1, AcDb::kForRead);
	if(!(pEnt1->isKindOf(AcDbLine::desc())   ||
		 pEnt1->isKindOf(AcDbCircle::desc()) ||
		 pEnt1->isKindOf(AcDbArc::desc())
		)
	  )
	{
		acutPrintf("\nNot a line, arc or circle entity.");
		pEnt1->close();
		return;
	}

	// Now get the second entity
	rc = acedEntSel("\nSelect second entity to test for intersection: ",
					ent, pickPt);
	switch(rc)
	{
		case RTCAN:
			acutPrintf("\nUser canceled.");
			return;
		break;

		case RTERROR:
			acutPrintf("\nNothing selected.");
			return;
		break;
	}// switch

	acdbGetObjectId(entId2, ent);
	acdbOpenAcDbEntity(pEnt2, entId2, AcDb::kForRead);

	// Is the second entity the same as the first?
	if(entId1 == entId2)
	{
		acutPrintf("\nSame entity selected!");
		pEnt1->close();
		pEnt2->close();
		return;
	}

	// Is the second entity a line, arc or circle?
	if(!(pEnt2->isKindOf(AcDbLine::desc())   ||
		 pEnt2->isKindOf(AcDbCircle::desc()) ||
		 pEnt2->isKindOf(AcDbArc::desc())
		)
	  )
	{
		acutPrintf("\nNot a line, arc or circle entity.");
		pEnt1->close();
		pEnt2->close();
		return;
	}

	// If the first entity is not a circle
	// ask if the user wants to extend the entity
	// for apparent intersection
	if(!pEnt1->isKindOf(AcDbCircle::desc()))
	{
		pEnt1->highlight();

		acedInitGet(NULL, "Yes No");
		rc = acedGetKword("\nExtend the highlighted entity - [Yes/No]<No>: ", kw);

		pEnt1->unhighlight();

		switch(rc)
		{
			case RTCAN:
			case RTERROR:
				acutPrintf("\nError with acedGetKword(). ");
				pEnt1->close();
				pEnt2->close();
				return;
			break;

			case RTNONE:
			break;

			case RTNORM:
				if(strcmp(kw, "Yes") == 0)
				{
					extVal = 1;
				}
			break;
		}
	}

	// If the second entity is not a circle
	// ask if the user wants to extend the entity
	// for apparent intersection
	if(!pEnt2->isKindOf(AcDbCircle::desc()))
	{
		pEnt2->highlight();

		acedInitGet(NULL, "Yes No");
		rc = acedGetKword("\nExtend the highlighted entity - [Yes/No]<No>: ", kw);

		pEnt2->unhighlight();

		switch(rc)
		{
			case RTCAN:
			case RTERROR:
				acutPrintf("\nError with acedGetKword(). ");
				pEnt1->close();
				pEnt2->close();
				return;
			break;

			case RTNONE:
			break;

			case RTNORM:
				if(strcmp(kw, "Yes") == 0)
				{
					if(extVal == 1)
					{
						extVal = 3;	// Both
					}
					else
					{
						extVal = 2;	// Second only
					}
				}
			break;
		}
	}


	// Does the first selected entity
	// intersect with the second enity?
	switch(extVal)
	{
		case 0 :
			pEnt1->intersectWith(pEnt2, AcDb::kOnBothOperands, intPoints);
			// No extension on either entity
		break;

		case 1 :
			pEnt1->intersectWith(pEnt2, AcDb::kExtendThis, intPoints);
			// Extend the first entity
		break;

		case 2 :
			pEnt1->intersectWith(pEnt2, AcDb::kExtendArg, intPoints);
			// Extend the second entity
		break;

		case 3 :
			pEnt1->intersectWith(pEnt2, AcDb::kExtendBoth, intPoints);
			// Extend both entities
		break;

	}

	pEnt1->close();
	pEnt2->close();

	// Here is an example of the AcGePoint3dArray class
	// as well as a demonstration of how to use translation
	// matricies AcGeMatrix3d
	if(! intPoints.isEmpty())
	{
		for(int i = 0; i < intPoints.length(); i++)
		{
			intPt = intPoints.at(i);

			transMat = AcGeMatrix3d(transVec);
			r1 = intPt;
			r1.transformBy(transMat);
			transVec.set(0.0, -0.2, 0.0);
			transMat = AcGeMatrix3d(transVec);
			r2 = r1;
			r2.transformBy(transMat);

			acedGrDraw((double*)(&r1), (double*)(&r2), 3, 0);

			transVec.set(0.2, 0.0, 0.0);
			transMat = AcGeMatrix3d(transVec);
			r3 = r2;
			r3.transformBy(transVec);

			acedGrDraw((double*)(&r2), (double*)(&r3), 3, 0);

			transVec.set(0.0, 0.2, 0.0);
			transMat = AcGeMatrix3d(transVec);
			r4 = r3;
			r4.transformBy(transVec);

			acedGrDraw((double*)(&r3), (double*)(&r4), 3, 0);
			acedGrDraw((double*)(&r4), (double*)(&r1), 3, 0);

			transVec.set(-0.1, 0.1, 0.0);

			acutPrintf("\nIntersection point = (%.2lf %.2lf %.2lf)",
						intPt.x, intPt.y, intPt.z);
		}
	}
}

// This is command 'OFCRVS'
void ofcrvs()
{
	AcDbDatabase *pCurDb;
	AcDbEntity *pEnt;
	AcDbCurve *pCurv;
	AcDbObjectId entId;
	AcDbVoidPtrArray offCurvs;

	AcDbBlockTable *pBlkTable;
	AcDbBlockTableRecord *pBlkTableRecord;

	double offVal;
	ads_name en;
	ads_point pickPt;

	Acad::ErrorStatus es;

	int rc;

	// Select the entity to offset
	rc = acedEntSel("\nSelect entity to offset: ", en, pickPt);
	switch(rc)
	{
		case RTCAN:
		case RTERROR:
			acutPrintf("\nError or nothing selected");
			return;
		break;
	}

	// Now get the offset distance either a positive
	// or negative number if fine, but not zero

	acedInitGet(RSG_NONULL + RSG_NOZERO, NULL);
	rc = acedGetReal("\nEnter offset distance - no zero allowed: ", &offVal);

	// Open the entity select by acedEntSel for
	// a read operation, don't forget to get the
	// objectId first, before attempting to open
	// the object, otherwise you will crash AutoCAD.
	// Not that I have ever done this of course!!
	acdbGetObjectId(entId, en);
	acdbOpenObject(pEnt, entId, AcDb::kForRead);

	// Check if the entity selectd is derived
	// from AcDbCurve
	if((pCurv = AcDbCurve::cast(pEnt)) == NULL)
	{
		acutPrintf("\nEntity select is not offsetable.");
		pEnt->close();
		return;
	}

	// Apply the offset curve, here is one area where
	// I always test the es return value. You could try to
	// offset the curve with an invalid value in which
	// case you get Acad::eInvalidInput. Another return code
	// is Acad::eNotImplementedYet which if for cases when
	// you have to override the getOffsetCurves, espically for
	// custom entities.
	es = pCurv->getOffsetCurves(offVal, offCurvs);
	if(es != Acad::eOk)
	{
		acutPrintf("\nError offseting curve");
		pEnt->close();
		return;
	}

	pEnt->close();

	if(!offCurvs.isEmpty())
	{
		pCurDb = acdbHostApplicationServices()->workingDatabase();
		// Open the Block Table and find the ACDB_MODEL_SPACE
		// recorcd.
		pCurDb->getBlockTable(pBlkTable, AcDb::kForRead);
		pBlkTable->getAt(ACDB_MODEL_SPACE, pBlkTableRecord, AcDb::kForWrite);
		pBlkTable->close();

		// Step through the offCurvs void pointer array
		for(int i = 0; i < offCurvs.length(); i++)
		{
			// cast the void pointer to an AcDbEntity pointer
			// add the entity close to the Block Table record
			// close the entity
			pEnt = (AcDbEntity*)offCurvs.at(i);
			pBlkTableRecord->appendAcDbEntity(entId, pEnt);
			pEnt->close();
		}

		// Close the Block Table Record
		pBlkTableRecord->close();
	}
}

